package com.eobard.controller;

import com.eobard.entity.Permission;
import com.eobard.entity.User;
import com.eobard.utils.JwtUtils;
import com.eobard.utils.MenuTree;
import com.eobard.utils.RedisUtils;
import com.eobard.utils.Result;
import com.eobard.vo.RouterVo;
import com.eobard.vo.TokenVo;
import com.eobard.vo.UserVo;
import io.jsonwebtoken.Jwts;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author Eobard_Thawne
 * @version 1.0
 * @date 2022/8/13 -16:46
 */
@RestController
@RequestMapping("api/sysUser")
public class SysUserController {

    @Resource
    private JwtUtils jwtUtils;

    @Resource
    private RedisUtils redisUtils;

    /**
     * 刷新token信息
     */
    @PostMapping("/refreshToken")
    public Result refreshToken(HttpServletRequest request) {
        //获取请求头的token
        String token = request.getHeader("token");
        //如果请求头没有token,尝试从请求参数中获取
        if (ObjectUtils.isEmpty(token)) {
            token = request.getParameter("token");
        }

        //若两个地方都没有token信息,则不能刷新token
        if (ObjectUtils.isEmpty(token)) {
            Result.error().code(600).message("刷新token失败!");
        }

        //从spring security上下文获取用户信息:类似于从session中拿
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        //获取用户信息
        UserDetails principal = (UserDetails)authentication.getPrincipal();

        String newToken=null;

        //验证原来的token是否合法:即token里面的用户名和数据库的用户名是否一致&&token不能过期
        if(jwtUtils.validateToken(token,principal)){
            //重新生成token
            newToken=jwtUtils.refreshToken(token);
            //从redis中删除旧的token信息
            redisUtils.del(TokenVo.USER_TOKEN_PREFIX+token);
            //在redis中存储新的token信息
            redisUtils.set(TokenVo.USER_TOKEN_PREFIX+newToken,newToken,jwtUtils.getExpiration()/1000);
        }

        //设置获取新的token过期时间,交给前端
        long expireTime = Jwts.parser()                                             //获取DefaultJwtParser对象
                .setSigningKey(jwtUtils.getSecret())                                //设置签名的密钥
                .parseClaimsJws(newToken.replace("jwt_", ""))      //生成的token默认有jwt_下标,将其替换
                .getBody().getExpiration().getTime();                               //获取token过期时间

        //返回信息
        TokenVo tokenVo=new TokenVo(expireTime,newToken);
        return Result.ok(tokenVo).message("token刷新成功");
    }


    /**
     * 获取登录用户个人信息
     */
    @GetMapping("/getInfo")
    public Result getUserInfo(){
        //根据token从spring security上下文获取用户信息:类似于从session中拿
        Authentication authentication=SecurityContextHolder.getContext().getAuthentication();

        if(ObjectUtils.isEmpty(authentication)){
            return Result.error().message("用户信息查询失败");
        }

        //获取用户信息
        User user=(User) authentication.getPrincipal();
        //获取用户的权限列表
        List<Permission> permissionList = user.getPermissionList();
        //获取用户的角色代号,即sys:user:add类型 转为数组形式
        Object[] roles = permissionList.stream()
                .filter(item -> item != null)
                .map(Permission::getCode)
                .toArray();

        //封装用户信息
        UserVo userVo = new UserVo(user.getId(), user.getNickName(), user.getAvatar(), null, roles);
        return Result.ok(userVo).message("用户查询成功");
    }


    /**
     * 获取菜单数据
     */
    @GetMapping("/getMenuList")
    public Result getMenuList(){
        //根据token从spring security上下文获取用户信息:类似于从session中拿
        Authentication authentication=SecurityContextHolder.getContext().getAuthentication();

        if(ObjectUtils.isEmpty(authentication)){
            return Result.error().message("用户菜单列表查询失败");
        }

        //获取用户信息
        User user=(User) authentication.getPrincipal();
        //获取用户的权限列表
        List<Permission> permissionList = user.getPermissionList();

        //筛选出目录和菜单:0代表顶级目录,1代表的是子菜单,2代表的是具体功能的按钮
        List<Permission> permissions = permissionList.stream()
                //排除按钮
                .filter(item -> item != null && item.getType() != 2)
                .collect(Collectors.toList());

        //生成路由数据
        List<RouterVo> routerVos = MenuTree.makeRouter(permissions, 0l);
        return Result.ok(routerVos).message("用户菜单列表查询成功!");
    }


    /**
     * 用户退出登录
     */
    @PostMapping("/logout")
    public Result logout(HttpServletRequest request, HttpServletResponse response){
        //获取请求头的token信息
        String token = jwtUtils.getTokenFromRequest(request);

        //从spring security上下文获取用户信息:类似于从session中拿
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        //判断用户是否为空
        if(authentication != null) {
            //清除用户信息
            new SecurityContextLogoutHandler().logout(request, response, authentication);
            //删除redis的用户缓存信息
            redisUtils.del(TokenVo.USER_TOKEN_PREFIX + token);
            return Result.ok().message("注销成功!");
        }else {
            return Result.error().message("注销失败,请稍后重试!");
        }
    }


}
